/***************************************************************************************
* Copyright (c) 2014-2022 Zihao Yu, Nanjing University
*
* NEMU is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*          http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
***************************************************************************************/
#include "include/sim.h"
#include "include/diff.h"
#include "include/utils.h"

#define MAXTIME 1000
int main_time = 0;  //initial 仿真时间
//void nvboard_bind_all_pins(Vtop* top);

VerilatedContext* contextp;
Vtop* dut;
VerilatedVcdC* tfp;

void single_cycle() {
  dut->clk = 0; dut->eval(); tfp->dump(main_time); //dump wave
  dut->clk = 1; dut->eval(); tfp->dump(main_time); //dump wave
}

void reset(int n) {
  dut->rst_n = 1;
  while (n -- > 0) single_cycle();
  dut->rst_n = 0;
}

void cpu_sim_init() {
  printf("initializing simulation\n");
  contextp = new VerilatedContext;
  //contextp->commandArgs(argc, argv);
  //dut = new Vtop{contextp};
  dut = new Vtop;
  //VCD波形设置  start
  Verilated::traceEverOn(true);
  tfp = new VerilatedVcdC;
  dut->trace(tfp, 0);
  tfp->open("wave.vcd");

  //cpu_gpr[32] = CONFIG_MBASE;
  dut -> clk = 0;
  dut -> rst_n = 0;
  dut -> eval();
  tfp->dump(main_time);
  main_time ++;
  dut -> clk = 1;
  dut -> rst_n = 0;
  dut -> eval();
  tfp->dump(main_time);
  main_time ++;
  dut -> rst_n = 1;
}

char logbuf[128];
void exec_once() {
  dut->clk = 0;
  //dut->instr = pmem_read(dut->pc,4);
  //dut->instr = 0x100073;
  printf("excute addr:0x%08x inst:0x%08x\n",dut->rootp->top__DOT__pc,dut->rootp->top__DOT__instr);

  #ifdef CONFIG_FTRACE
    //if(dut->rootp->top__DOT__instr >> 26 == 0x67) //jalr
    //  if(rd == 1) //ra=return address
    //    ftrace_call (s->pc , s->dnpc);
    //  else if(rd == 0 && src1 == R(1))
    //    ftrace_ret(dut->rootp->top__DOT__pc);
  #endif

  #ifdef CONFIG_ITRACE
    char *p = logbuf;
    p += snprintf(p, sizeof(logbuf), "0x%08x: 0x%08x ", dut->rootp->top__DOT__pc, dut->rootp->top__DOT__instr);
    *p = '\0';
    iringbuf_write(dut->rootp->top__DOT__pc,logbuf);
    //printf("%s \n",iring.logbuf[0]);
  #endif
  
  dut->eval();
  tfp->dump(main_time);
  main_time ++;
  dut->clk = 1;
  dut->eval(); 
	tfp->dump(main_time);
  main_time ++;

  
  update_regs();
  cpu_state.pc = dut->rootp->top__DOT__pc;

  #ifdef CONFIG_DIFFTEST
    ref_difftest_exec(1);
    if(!isa_difftest_checkregs())
      npc_state.state = NPC_ABORT;
  #endif

  if(npc_state.state == NPC_END){
        printf("NPC simulation finished at cycle = %d, a0 = %d, ", main_time, dut->rootp->top__DOT__u_register_file__DOT__reg_file[10]);
        if(dut->rootp->top__DOT__u_register_file__DOT__reg_file[10] == 0){
            printf("HIT GOOD TRAP\n");
        }else{
            printf("HIT BAD  TRAP\n");
        }
    }
}

extern int cpu_pmem_read(int raddr)
{
  //printf("In raddr: %x\n",raddr);
  if(dut->clk == 1)
  {
    int data;
    data = pmem_read(raddr, 4);
    return data; 
  }
  else
    return 0;
}

void cpu_pmem_write(int waddr, int wdata, char wmask)
{
  //printf("In \n");
  if(dut->clk == 1){
    switch (wmask)
    {
      case 1: pmem_write(waddr, 1, wdata);
                  break;
      case 2: pmem_write(waddr, 2, wdata);
                  break;
      case 4: pmem_write(waddr, 4, wdata);
                  break;
      default:    assert(0);
                  break;
    }
  }
  else
    return;
}

extern "C" void invalid_inst() {
  uint32_t temp[2];
  paddr_t pc = dut->rootp->top__DOT__pc;
  temp[0] = pmem_read(pc, 4);
  temp[1] = pmem_read(pc, 4);

  uint8_t *p = (uint8_t *)temp;
  printf("\t%02x %02x %02x %02x %02x %02x %02x %02x ...\n \t%08x %08x...\n",
            p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], temp[0], temp[1]);

  printf("1. The instruction at PC = %x is not implemented.\n 2. Something is implemented incorrectly.\n", pc);
  printf("Find this PC:%x in the disassembling result to distinguish which case it is.\n", pc);

  npc_state.state = NPC_ABORT;
}